package com.jumper.property.comparer.starter.jpa;

import com.jumper.property.comparer.core.enums.LogTable;
import com.jumper.property.comparer.core.support.LogRepository;
import com.jumper.property.comparer.core.support.param.LogPageParam;
import com.jumper.property.comparer.core.support.result.LogEntity;
import com.jumper.property.comparer.core.support.result.LogEntityPage;
import lombok.AllArgsConstructor;
import org.springframework.transaction.support.TransactionTemplate;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;


@AllArgsConstructor
public class JpaLogRepositoryImpl implements LogRepository {

    private final EntityManager entityManager;
    private final TransactionTemplate template;

    @Override
    public void save(LogTable busType, List<LogEntity> optLogs) {
        optLogs.forEach(optLog -> {
            template.execute(status -> {
                try {
                    insert(busType.getLogTableName(), optLog);
                } catch (Exception e) {
                    status.setRollbackOnly();
                    throw e;
                }
                return null;
            });
        });
    }

    @Override
    public LogEntityPage getLogsPage(LogTable busType, LogPageParam pageParam) {
        List<LogEntity> logs = new ArrayList<>();
        int pageNum = pageParam.getPageNum();
        int pageSize = pageParam.getPageSize();
        int offset = ((pageNum - 1) * pageSize);
        int count = getCount(busType.getLogTableName(), pageParam.getDataKey());
        if (count > 0) {
            logs = select(busType.getLogTableName(), pageParam.getDataKey(), offset, pageSize);
        }
        return new LogEntityPage(pageNum, pageSize, count, logs);
    }

    private List<LogEntity> select(String tableName, String dataKey, int offset, int pageSize) {
        String listSQL = "select t1.id , t1.gmt_created, t1.gmt_modified, t1.action, t1.data_key, t1.log_value, t1.opt_user_id, t1.opt_user_name from {tableName} t1 where t1.data_key = :dataKey order by t1.gmt_created desc limit :offset,:pageSize"
                .replace("{tableName}", tableName);
        Query nativeQuery = entityManager.createNativeQuery(listSQL);
        nativeQuery.setParameter("dataKey", dataKey);
        nativeQuery.setParameter("offset", offset);
        nativeQuery.setParameter("pageSize", pageSize);
        List<Object[]> resultList = nativeQuery.getResultList();
        return resultList.stream().map(objects -> {
            LogEntity busOptLog = new LogEntity();
            busOptLog.setId(((BigInteger) objects[0]).longValue());
            busOptLog.setGmtCreated(((Timestamp) objects[1]).toLocalDateTime());
            busOptLog.setGmtModified(((Timestamp) objects[2]).toLocalDateTime());
            busOptLog.setAction(objects[3].toString());
            busOptLog.setDataKey(objects[4].toString());
            busOptLog.setLogValue(objects[5].toString());
            busOptLog.setOptUserId(((BigInteger) objects[6]).longValue());
            busOptLog.setOptUserName(objects[7].toString());
            return busOptLog;
        }).collect(Collectors.toList());
    }

    /**
     * 查询总量
     */
    private int getCount(String tableName, String dataKey) {
        String countSQL = "select count(*) from {tableName} where data_key = :dataKey"
                .replace("{tableName}", tableName);
        Query nativeQuery = entityManager.createNativeQuery(countSQL);
        nativeQuery.setParameter("dataKey", dataKey);
        return ((BigInteger) nativeQuery.getSingleResult()).intValue();
    }

    /**
     * 插入一条操作日志
     */
    private void insert(String tableName, LogEntity optLog) {
        String insertSQL = "insert into {tableName} (gmt_created, gmt_modified, action, data_key, log_value, opt_user_id, opt_user_name) values (:gmtCreated,:gmtModified,:action,:dataKey,:logValue,:optUserId,:optUserName)"
                .replace("{tableName}", tableName);
        Query nativeQuery = entityManager.createNativeQuery(insertSQL);
        nativeQuery.setParameter("gmtCreated", optLog.getGmtCreated());
        nativeQuery.setParameter("gmtModified", optLog.getGmtModified());
        nativeQuery.setParameter("action", optLog.getAction());
        nativeQuery.setParameter("dataKey", optLog.getDataKey());
        nativeQuery.setParameter("logValue", optLog.getLogValue());
        nativeQuery.setParameter("optUserId", optLog.getOptUserId());
        nativeQuery.setParameter("optUserName", optLog.getOptUserName());
        nativeQuery.executeUpdate();
    }
}
